/**   
* @Title: SyncClientManager.java 
* @Package net.gdface.httpclient 
* @Description: TODO 
* @author guyadong   
* @date 2015年5月28日 下午4:19:31 
* @version V1.0   
*/
package net.gdface.httpclient;

import java.io.IOException;
import java.net.URI;

import net.gdface.utils.Assert;

import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.http.Header;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author guyadong
 *
 */
public class SyncClientManager {
	private static final Logger logger = LoggerFactory.getLogger(SyncClientManager.class);
	private static SyncClientManager singleton=null;	
	/**
	 * @return singleton
	 */
	public static SyncClientManager getInstance() {
		if (null == singleton)
			throw new IllegalArgumentException(
					"instance没有初始化, 请先执行  createSingleton(int maxConnection, int maxConnectionPerHost)创建实例");
		return singleton;
	}
	public static SyncClientManager createSingleton(int maxConnection, int maxConnectionPerHost) {
		if (null == singleton)
			singleton = new SyncClientManager(maxConnection,maxConnectionPerHost);
		return singleton;
	}
	/**
	 * 连接管理器最大连接数
	 */
	private final int maxConnections;
	/**
	 * 每个主机最大连接数
	 */
	private final int maxConnectionsPerHost;	
	/**
	 * httpclient对象
	 */
	private final CloseableHttpClient httpClient ;
	/**
	 * @param maxConnections
	 * @param maxConnectionsPerHost
	 */
	private SyncClientManager(int maxConnections, int maxConnectionsPerHost) {
		this.maxConnections = maxConnections>0?maxConnections:HttpClientUtility.DEFAULT_MAX_CONNECTIONS;
		this.maxConnectionsPerHost = maxConnectionsPerHost>0?maxConnectionsPerHost:HttpClientUtility.DEFAULT_MAX_CONNECTIONS_PER_HOST;

		httpClient = HttpClients.custom()
			.setDefaultRequestConfig(HttpClientUtility.BASE_REQUEST_CONFIG)
			//设置自定义的kee-alive策略 默认5秒
			.setKeepAliveStrategy(FixedKeepAliveStrategy.DEFAULT_5SECONDS)
			// 关闭连接重用，减少连接资源占用
			//.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE)
			//设置连接管理器最大连接数和每个主机最大连接数
			.setMaxConnPerRoute(this.maxConnectionsPerHost)
			.setMaxConnTotal(this.maxConnections)
			.build();
		logger.info("SyncClient maxConnections(同步客户端最大连接数)=[{}],maxConnectionsPerHost(每主机最大连接数)=[{}]",
				this.maxConnections, this.maxConnectionsPerHost);

	}


	private CloseableHttpClient getClient() {
		return httpClient;
	}
	public void close() throws IOException {
		httpClient.close();
		logger.info("SyncClient closed (同步客户端关闭)");
	}
	/**
	 * 获取指定地址{@code URI}的 {@code referer}
	 * 
	 * @param uri
	 * @param intervalMills
	 *            小于0时使用 {@link HostManager#DEFAULT_PAGE_INTERVAL_MILLS}作为缺省值
	 * @return
	 * @throws FailOpenPage
	 *             TODO
	 * @throws InterruptedException
	 * @see SyncClientManager#getResponseBlocked(URI, RequestConfig, long, NameValuePair...)
	 */
	public String getLocation(URI uri, long intervalMills) throws FailOpenPage, InterruptedException {
		// 不允许重定向，否则返回null
		CloseableHttpResponse resp = null;
		try {
			resp = getResponseBlocked(uri, RequestConfig.copy(HttpClientUtility.BASE_REQUEST_CONFIG).setRedirectsEnabled(false).build(),
					intervalMills < 0 ? HostManager.DEFAULT_PAGE_INTERVAL_MILLS : intervalMills);
			try {
				EntityUtils.toString(resp.getEntity());
			} catch (IOException e) {
				// 不影响打开图片
			}
			Header location = resp.getLastHeader(HTTPConstants.HEADER_LOCATION);
			return (null == location) ? null : location.getValue();
		} finally {
			if (null != resp)
				try {
					resp.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
		}
	}
	/**
	 * @param uri
	 * @param config
	 * @param intervalMills
	 * @param headers
	 * @return
	 * @throws FailOpenPage
	 * @throws InterruptedException
	 * @see SyncClientManager#getResponseBlocked(URI, RequestConfig, long, NameValuePair...)
	 */
	public String getPage(URI uri, RequestConfig config, long intervalMills, NameValuePair... headers)
			throws FailOpenPage, InterruptedException {
		CloseableHttpResponse resp = null;
		try {
			// 向搜索引擎请求文本页面
			resp = getResponseBlocked(uri, config, 0 > intervalMills ? HostManager.DEFAULT_PAGE_INTERVAL_MILLS
					: intervalMills, headers);
			return EntityUtils.toString(resp.getEntity(), "utf-8");
		} catch (ParseException e) {
			throw new FailOpenPage(e);
		} catch (IOException e) {
			throw new FailOpenPage(e);
		} finally {
			if (null != resp)
				try {
					resp.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
		}
	}
	public String getPage(URI uri, RequestConfig config, NameValuePair... headers){
		CloseableHttpResponse pageResp = null;
		try {
			pageResp = getResponse(uri,config,headers);
			return EntityUtils.toString(pageResp.getEntity());
		} catch (IOException e) {
			return null;
		} finally {
			if (null != pageResp)
				try {
					pageResp.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
		}
	}
	/**
	 * (同步)向服务器发送HTTP请求
	 * 
	 * @param uri
	 * @param config
	 *            ==null时使用默认的配置对象 {@link HttpClientUtility#BASE_REQUEST_CONFIG}
	 * @param headers
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @see SyncClientManager#getResponse(CloseableHttpClient, URI, RequestConfig, NameValuePair...)
	 */
	public CloseableHttpResponse getResponse(URI uri, RequestConfig config, NameValuePair... headers)
			throws ClientProtocolException, IOException {
		return getResponse(getClient(), uri, config, headers);
	}
	/**
	 * 同步阻塞方式发送GET请求 使用 {@code intervalMills }指定的间隔向服务器发送请求<br>
	 * 用于打开文本页面
	 * @param uri
	 * @param config 
	 * @param intervalMills
	 * @param headers
	 * @return
	 * @throws FailOpenPage 打开页面异常
	 * @throws InterruptedException
	 * @see SyncClientManager#getResponse(CloseableHttpClient, URI, RequestConfig, NameValuePair...)
	 */
	public CloseableHttpResponse getResponseBlocked(URI uri, RequestConfig config, long intervalMills,
			NameValuePair... headers) throws FailOpenPage, InterruptedException {
		try {
			HostManager.getInstance().sleepUnlessTimeup(uri, intervalMills, 0, 0);
			return getResponse(getClient(), uri, config, headers);
		} catch (IOException e) {
			throw new FailOpenPage(e);
		}
	}
	/**
	 * 调用{@link CloseableHttpClient}发送url请求,返回响应对象{@link CloseableHttpResponse}。<br>
	 * 
	 * @param client
	 * @param uri
	 * @param config
	 *            配置对象 {@link RequestConfig}
	 * @param headers
	 *            请求头键值对
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @see CloseableHttpClient#execute(org.apache.http.client.methods.HttpUriRequest)
	 */
	public CloseableHttpResponse getResponse(CloseableHttpClient client, URI uri, RequestConfig config,
			NameValuePair... headers) throws ClientProtocolException, IOException {
		Assert.notNull(client, "client");
		HttpGet get = HttpClientUtility.createHttpGET(uri,config,headers);
		CloseableHttpResponse resp = null;
		try {
			resp = client.execute(get);
			return resp;
		} finally {
			HostManager.getInstance().hitInc(uri, null != resp);
		}
	}
	/**
	 * @return maxConnections
	 */
	public int getMaxConnections() {
		return maxConnections;
	}

}
